package pide.gui;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextPane;
import javax.swing.JToolBar;
import javax.swing.KeyStroke;
import javax.swing.ListModel;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.UndoableEditEvent;
import javax.swing.event.UndoableEditListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultHighlighter;
import javax.swing.undo.CannotUndoException;
import javax.swing.undo.UndoManager;

import pide.core.Configuration;
import pide.core.Core;
import debug.DebugMessage;


public class FileView extends CenteredJFrame{

	static KeyStroke stroke_z_ctrl = KeyStroke.getKeyStroke(KeyEvent.VK_Z,Toolkit.getDefaultToolkit().getMenuShortcutKeyMask());
	 static DefaultHighlighter.DefaultHighlightPainter quote = 
	            new DefaultHighlighter.DefaultHighlightPainter(new Color(150,150,255));
	 
	 static DefaultHighlighter.DefaultHighlightPainter comments = 
	            new DefaultHighlighter.DefaultHighlightPainter(new Color(200,255,200));
	    
	 static    DefaultHighlighter.DefaultHighlightPainter marker1 = //:-
	            new DefaultHighlighter.DefaultHighlightPainter(new Color(150,150,150));
	 static    DefaultHighlighter.DefaultHighlightPainter marker2 = 
	            new DefaultHighlighter.DefaultHighlightPainter(new Color(255,255,150));
	 static    DefaultHighlighter.DefaultHighlightPainter markerDOT = 
	            new DefaultHighlighter.DefaultHighlightPainter(new Color(51,51,255));
	 static   DefaultHighlighter.DefaultHighlightPainter markerCOMMA = 
	            new DefaultHighlighter.DefaultHighlightPainter(new Color(204,204,255));
	 static    DefaultHighlighter.DefaultHighlightPainter markerSEMICOLON = 
	            new DefaultHighlighter.DefaultHighlightPainter(new Color(255,102,255));
	 static    DefaultHighlighter.DefaultHighlightPainter markerSearch =
			 new DefaultHighlighter.DefaultHighlightPainter(new Color(102,153,255));
	 
	 private UndoManager undo =new UndoManager();
	/****************************/
	
	private JToolBar toolbar;
	private JTextPane textarea;
//	private JTextPane outline;
	private JList<OutlineRecord> outline;
	
	private JButton save = new JButton("Save (Ctrl+S)");
	private JButton find = new JButton("Find (Ctrl+F)");
	private JButton exit = new JButton("Exit");
	private JButton run = new JButton("Run (F5)");
	
	private File file;
	private boolean changed = false;
	private String searched_word = null; 
	public FileView(File f)
	{
		file=f;
		toolbar = new JToolBar();
		
		
		
		prepareTextArea();
		prepareToolbar();
		prepareOutline();
		addStyle();
		JScrollPane scrollPane = new JScrollPane(textarea);				
		TextLineNumber tln = new TextLineNumber(textarea);
		scrollPane.setRowHeaderView(tln);
		
		
		changed=false;
		
		
		
		
		
		updateTitle();
		setLayout(new BorderLayout());		
		
		Configuration conf = Configuration.getInstance();
		
		setMinimumSize(new Dimension(conf.getInt("ide.min_width"), conf.getInt("ide.min_height")));
		outline.setMinimumSize(new Dimension((int)(conf.getInt("ide.min_width")*.2), conf.getInt("ide.min_height")));
		textarea.setMinimumSize(new Dimension((int)(conf.getInt("ide.min_width")*.8), conf.getInt("ide.min_height")));
		
		JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,scrollPane,new JScrollPane(outline));
		splitPane.setDividerLocation((int)(conf.getInt("ide.min_width")*.8));
		
		add(toolbar,BorderLayout.NORTH);
		add(splitPane,BorderLayout.CENTER);
//		add(splitPane,BorderLayout.EAST);
		
		
		
		
		
//		setMaximumSize(Toolkit.getDefaultToolkit().getScreenSize());
//		setMaximumSize(new Dimension(conf.getInt("ide.max_width"), conf.getInt("ide.max_height")));
		
		
		
		
		setSize(getMinimumSize());
		setVisible(true);
		setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
		
		
		
		
		addWindowListener(new WindowListener() {
			
			@Override
			public void windowOpened(WindowEvent arg0) {
				// TODO Auto-generated method stub
				
			}
			
			@Override
			public void windowIconified(WindowEvent arg0) {
				// TODO Auto-generated method stub
				
			}
			
			@Override
			public void windowDeiconified(WindowEvent arg0) {
				// TODO Auto-generated method stub
				
			}
			
			@Override
			public void windowDeactivated(WindowEvent arg0) {
				// TODO Auto-generated method stub
				
			}
			
			@Override
			public void windowClosing(WindowEvent arg0) {
				DebugMessage.info("windowClosing on "+this.getClass().getName());
				exit();
				
			}
			
			@Override
			public void windowClosed(WindowEvent arg0) {
				// TODO Auto-generated method stub
				
			}
			
			@Override
			public void windowActivated(WindowEvent arg0) {
				// TODO Auto-generated method stub
				
			}
		});
	}
	
	
	
	
	private void prepareOutline() {
		outline = new JList<OutlineRecord>();
		
		outline.addListSelectionListener(new ListSelectionListener() {
			
			@Override
			public void valueChanged(ListSelectionEvent arg) {
				if(!arg.getValueIsAdjusting() && outline.getSelectedValue()!=null){
				System.out.println(outline.getSelectedIndex());
				DebugMessage.debug(""+outline.getSelectedValue().idx);
				moveTextare(outline.getSelectedValue().idx-1);
				searched_word=outline.getSelectedValue().msg;
				addStyle();
				
				}
			}
		});
		
	}

	private void moveTextare(int i)
	{
		textarea.setCaretPosition(textarea.getDocument().getDefaultRootElement().getElement(i).getStartOffset());		
		textarea.validate();
	}
	
	private void updateTitle()
	{
		setTitle(file.getName()+(changed?"*":"")+" ("+file.getAbsolutePath()+")");
		validate();
	}
	
	private void prepareToolbar()
	{
		toolbar.setOrientation(JToolBar.HORIZONTAL);
		save.addActionListener(new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent e) {
				save();				
			}
		});

		
		run.addActionListener(new ActionListener() {			
			@Override
			public void actionPerformed(ActionEvent arg0) {				
				Core.getDefault().run(file.getAbsolutePath());			
			}
		});
		
		
		
		exit.addActionListener(new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent e) {
				exit();
			}
		});
		
		
		find.addActionListener(new ActionListener() {			
			@Override
			public void actionPerformed(ActionEvent arg0) {				
				searchWord();
			}
		});
		
		toolbar.add(save);
		toolbar.add(run);
		toolbar.add(find);
		toolbar.add(exit);
	}
	
	protected void searchWord() {
		searched_word = JOptionPane.showInputDialog("find :");
		addStyle();
	}




	private void prepareTextArea()
	{
		textarea = new JTextPane();
		String txt = loadPL();
//		DebugMessage.info(txt);
		textarea.setText(txt);	
		
//		textarea.setWrapStyleWord(true);
		
		textarea.setFont(new Font("Consolas", Font.PLAIN, 11));
		
		textarea.getDocument().addDocumentListener(new DocumentListener() {
			
			@Override
			public void removeUpdate(DocumentEvent e) {
				changedUpdate(e);
			}
			
			@Override
			public void insertUpdate(DocumentEvent e) {
				changedUpdate(e);
			}
			
			@Override
			public void changedUpdate(DocumentEvent e) {
				
				
				addStyle();
				validate();		
				
				
			}
		});
		
		textarea.addKeyListener(new KeyListener() {
			
			@Override
			public void keyTyped(KeyEvent arg0) {				
				if(!changed)
				{
					changed=true;
					updateTitle();
				}
//				
			}
			
			@Override
			public void keyReleased(KeyEvent arg0) {				
			}
			
			@Override
			public void keyPressed(KeyEvent arg) {		
//				System.out.println(arg);
				
				switch (arg.getKeyCode()) {
				case KeyEvent.VK_F5:
					save();
					Core.getDefault().run(file.getAbsolutePath());	
					break;				
				default:
					break;
			}
		}
		});
		
		
			textarea.getDocument().addUndoableEditListener(new UndoableEditListener() {
			      public void undoableEditHappened(UndoableEditEvent evt) {
			        undo.addEdit(evt.getEdit());
			      }
			    });

		    textarea.getActionMap().put("Undo", new AbstractAction("Undo") {
		      public void actionPerformed(ActionEvent evt) {
		        try {
		          if (undo.canUndo()) {
		            undo.undo();
		          }
		        } catch (CannotUndoException e) {
		        }
		      }
		    });
		    
		    textarea.getActionMap().put("Save", new AbstractAction("Save") {
		        public void actionPerformed(ActionEvent evt) {
		         save();
		         changed=false;
		        }
		      });
		    
		    textarea.getActionMap().put("Find", new AbstractAction("Find") {
		        public void actionPerformed(ActionEvent evt) {
		        	searchWord();
		        }
		      });

		    textarea.getInputMap().put(KeyStroke.getKeyStroke("control Z"), "Undo");
		    textarea.getInputMap().put(KeyStroke.getKeyStroke("control S"), "Save");
		    textarea.getInputMap().put(KeyStroke.getKeyStroke("control F"), "Find");
		
		
		
		
	}
	
	
	private void addStyle()
	{
//		StyledDocument doc = textarea.getStyledDocument();
		String txt = textarea.getText();
		textarea.getHighlighter().removeAllHighlights();
//		 SimpleAttributeSet sas = new SimpleAttributeSet();
//		    StyleConstants.setForeground(sas, Color.YELLOW);
//		    doc.setCharacterAttributes(start, length, sas, false);
		    
		   
	
		    
		    
		    try {
		    		    	
//		    	for(int[] array :  findQuote(txt))
//		    	{		    		
//		    		textarea.getHighlighter().addHighlight(array[0], array[1], quote);
//		    	
//		    	}
		    	
		    	for(int[] array :  findComments(txt))
		    	{		    		
		    		textarea.getHighlighter().addHighlight(array[0], array[1], comments);
		    	
		    	}
		    	
		    	for(int[] array :  findMarker1(txt))
		    	{		    		
		    		textarea.getHighlighter().addHighlight(array[0], array[1], marker1);
		    		
		    	}
		    	
		    	for(int[] array :  findMarker2(txt))
		    	{		    		
		    		textarea.getHighlighter().addHighlight(array[0], array[1], marker2);
		    	}
		    	
		    	for(int[] array :  findMarkerDOT(txt))
		    	{		    		
		    		textarea.getHighlighter().addHighlight(array[0], array[1], markerDOT);
		    	}
		    	
		    	for(int[] array :  findMarkerCOMMA(txt))
		    	{		    		
		    		textarea.getHighlighter().addHighlight(array[0], array[1], markerCOMMA);
		    	}
		    	
		    	for(int[] array :  findMarkerSEMICOLON(txt))
		    	{		    		
		    		textarea.getHighlighter().addHighlight(array[0], array[1], markerSEMICOLON);
		    	}
		    	
		    	for(int[] array :  search(txt))
		    	{		    		
		    		textarea.getHighlighter().addHighlight(array[0], array[1], markerSearch);
		    	
		    	}
		    
		    	
		    	///spostare
		    	
		    	HashMap<String, Integer> map = findPredicate(txt);
//		    	String outline_str="";
		    	outline.removeAll();
		    	
		    	ListModel<OutlineRecord> lmodel = OutlineRecord.create(map);
		    	if(lmodel!=null)
		    		outline.setModel(lmodel);
//		    	System.out.println(outline.getModel().getSize());
//		    	for(String s: map.keySet())
//		    	{
//		    		outline_str+=(s+"("+(map.get(s)+1)+")\n");
//		    	}
//		    	outline.setText(outline_str);
		    	outline.validate();
		    	
		    	
		    	
			} catch (BadLocationException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
	}
	
	private List<int[]>findQuote(String txt)
	{
		int offset = 0  ;
		List<int[]> list = new ArrayList<>();
		while(offset<txt.length())
		{
			offset = txt.indexOf('\'',offset);
			if(offset>-1)
			{
				int newoffset = txt.indexOf('\'',offset+1);
				if(newoffset>-1)
				{
					list.add(new int[]{offset,newoffset+1});
					offset=newoffset+1;
				}
				else
				{
					
					list.add(new int[]{offset,txt.length()});
					offset=txt.length();
				}
			}
			else
			{
				offset=txt.length();
			}
			
			
		}
		return list;
	}
	
	private List<int[]>findComments(String txt)
	{
		int offset = 0  ;
		List<int[]> list = new ArrayList<>();
		while(offset<txt.length())
		{
			offset = txt.indexOf('%',offset);
			if(offset>-1)
			{
				int newoffset = txt.indexOf('\n',offset+1);
				if(newoffset>-1)
				{
					list.add(new int[]{offset,newoffset+1});
					offset=newoffset+1;
				}
				else
				{
					
					list.add(new int[]{offset,txt.length()});
					offset=txt.length();
				}
			}
			else
			{
				offset=txt.length();
			}
			
			
		}
		return list;
	}
	
	
	
	private List<int[]>findMarker1(String txt)
	{
		int offset = 0  ;
		List<int[]> list = new ArrayList<>();
		while(offset>-1)
		{
			offset = txt.indexOf(":-",offset);
			if(offset>-1)
			{
				
				list.add(new int[]{offset,offset+2});
				offset=offset+1;
				
			}
			else
			{
				offset=-1;
			}
			
			
		}
		return list;
	}

	private List<int[]>findMarkerCOMMA(String txt)
	{
		int offset = 0  ;
		List<int[]> list = new ArrayList<>();
		while(offset>-1)
		{
			offset = txt.indexOf(",",offset);
			if(offset>-1)
			{
				
				list.add(new int[]{offset,offset+1});
				offset=offset+1;
				
			}
			else
			{
				offset=-1;
			}
			
			
		}
		return list;
	}
	
	private List<int[]>findMarker2(String txt)
	{
		int offset = 0  ;
		List<int[]> list = new ArrayList<>();
		while(offset>-1)
		{
			offset = txt.indexOf("?-",offset);
			if(offset>-1)
			{
				
				list.add(new int[]{offset,offset+2});
				offset=offset+1;
				
			}
			else
			{
				offset=-1;
			}
			
			
		}
		return list;
	}
	
	private List<int[]>findMarkerDOT(String txt)
	{
		int offset = 0  ;
		List<int[]> list = new ArrayList<>();
		while(offset>-1)
		{
			offset = txt.indexOf(".",offset);
			if(offset>-1)
			{
				
				list.add(new int[]{offset,offset+1});
				offset=offset+1;
				
			}
			else
			{
				offset=-1;
			}
			
			
		}
		return list;
	}
	
	private List<int[]>findMarkerSEMICOLON(String txt)
	{
		int offset = 0  ;
		List<int[]> list = new ArrayList<>();
		while(offset>-1)
		{
			offset = txt.indexOf(";",offset);
			if(offset>-1)
			{
				
				list.add(new int[]{offset,offset+1});
				offset=offset+1;
				
			}
			else
			{
				offset=-1;
			}
			
			
		}
		return list;
	}

	private List<int[]> search(String txt)
	{
		List<int[]> list = new ArrayList<>();
		if(searched_word==null)
			return list;
		
		int offset = 0  ;	
		txt=txt.toLowerCase();
		searched_word=searched_word.toLowerCase();
		while(offset>-1)
		{
			offset = txt.indexOf(searched_word,offset);
			if(offset>-1)
			{
				
				list.add(new int[]{offset,offset+searched_word.length()});
				offset=offset+1;
				
			}
			else
			{
				offset=-1;
			}
			
			
		}
		
		searched_word=null;
		return list;
	}

	private HashMap<String,Integer> findPredicate(String txt)
	{
		
		HashMap<String,Integer> map = new HashMap<>();
		
		String []array = txt.split("\n");
		for(int i = 0;i<array.length;i++){
			array[i]=array[i].trim();
			if(!array[i].startsWith("%") && !array[i].startsWith("?-") && !array[i].startsWith(":-"))
			{
				int offset = array[i].indexOf(":-");
				if(offset>-1)
				{
					String predicato = array[i].substring(0,offset);
					//trovo la prima parentesi (
					offset = predicato.indexOf("(");
					if(offset>-1)
					{
						map.put(predicato.substring(0,offset),i);	
					}
										
					
				}
//				else
//				{
//					offset = array[i].indexOf(".");
//					if(offset>-1)
//					{
//						String predicato = array[i].substring(0,offset);
//						//trovo la prima parentesi (
//						offset = predicato.indexOf("(");
//						if(offset>-1)							
//								map.put(predicato.substring(0,offset),i);	
//					}
//				}
			}
		}
		
		return map;
	}
	
	
	
	private int countCOMMA(String txt)
	{
		return txt.split(",").length-1;
	}
	
//	private void run()
//	{
//		if(changed)
//		{
//			save();
//			
//		}
//		
//		//run;
//	}
	
	private void save()
	{
		
		PrintWriter pw;
		try {
			pw = new PrintWriter(file);
			pw.print(textarea.getText());
			pw.close();
			DebugMessage.info("Save [ ok ]");
			changed=false;
			updateTitle();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			DebugMessage.error("Save [ fail ]\n"+e.getMessage());
		}
		
	}
	
	private void exit()
	{
		if(changed)
		{	
			
			int res = JOptionPane.showConfirmDialog(
					this, 
					"There are change in your file. Save?"				
							);
			DebugMessage.debug("JOptionPane [ "+res+" ]");
			switch (res) {
			case JOptionPane.OK_OPTION://yes
				save();
				Core.getDefault().closeFileWiew(file);
				dispose();
				break;
			case JOptionPane.NO_OPTION: //no
				dispose();
				break;			
			default:
				break;
			}
		}
		else
		{
			Core.getDefault().closeFileWiew(file);
			dispose();
		}
		
	}
	
	private String loadPL() 
	{
		String str = "";
		try{
			BufferedReader in = new BufferedReader(new FileReader(file)) ;			
			String line = null;
			while((line=in.readLine())!=null)
			{
				str+=line+"\n";
			}
			in.close();
		}
		catch(Exception e)
		{
			DebugMessage.error(e.getMessage());
		}
		return str;
	}
	
}


class OutlineRecord
{
	String msg;
	int idx;
	public OutlineRecord(String msg, int idx) {
		super();
		this.msg = msg;
		this.idx = idx;
	}
	
	public static ListModel<OutlineRecord> create(HashMap<String, Integer> map)
	{
		OutlineRecord[] out = new OutlineRecord[map.size()];
		int i = 0;
		for(String s: map.keySet())
    	{
    		out[i++]=new OutlineRecord(s,(map.get(s)+1));
    	}
	
		if(i==0)
			return null;
		
		return new JList<>(out).getModel();
		
		
	}
	
	
	@Override
	public String toString() {
		return msg;
	}
}